package com.talent.web.utils;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @ClassName：OrderIdGenerator
 * @Author: hszhou
 * @Date: 2025/3/26 15:13
 * @Description: 订单生成器
 */
public class OrderIdGenerator {

    /**基础配置（可配置化）*/
    private static final String DATE_FORMAT = "yyyyMMddHHmmss";
    /**机器ID位数（最多支持8台机器）*/
    private static final int MACHINE_ID_BITS = 3;
    /**序列号位数（每毫秒最多16个）*/
    private static final int SEQUENCE_BITS = 4;

    // 运行时参数
    /** 机器ID（0-7）*/
    private final int machineId;
    private final AtomicInteger sequence = new AtomicInteger(0);
    private volatile long lastTimestamp = -1L;


    /**
     * 初始化（建议使用依赖注入）
     */
    public OrderIdGenerator(int machineId) {
        if (machineId < 0 || machineId >= (1 << MACHINE_ID_BITS)) {
            throw new IllegalArgumentException("机器ID范围错误");
        }
        this.machineId = machineId;
    }

    /**
     * 生成订单号（线程安全）
     */
    public synchronized String generate() {
        long currentTimestamp = System.currentTimeMillis();

        // 时钟回拨检测
        if (currentTimestamp < lastTimestamp) {
            throw new RuntimeException("时钟回拨异常，拒绝生成ID");
        }

        // 同一时间单位内递增序列号
        if (currentTimestamp == lastTimestamp) {
            int seq = sequence.incrementAndGet();
            if (seq >= (1 << SEQUENCE_BITS)) {
                // 当前毫秒序列号用尽，等待下一毫秒
                currentTimestamp = waitNextMillis(currentTimestamp);
                sequence.set(0);
            }
        } else {
            sequence.set(0);
        }

        lastTimestamp = currentTimestamp;

        return buildOrderId(currentTimestamp);
    }

    private long waitNextMillis(long currentTimestamp) {
        while (currentTimestamp == lastTimestamp) {
            currentTimestamp = System.currentTimeMillis();
        }
        return currentTimestamp;
    }

    private String buildOrderId(long timestamp) {
        LocalDateTime dateTime = LocalDateTime.ofInstant(
                java.time.Instant.ofEpochMilli(timestamp),
                java.time.ZoneId.systemDefault()
        );

        String timePart = dateTime.format(DateTimeFormatter.ofPattern(DATE_FORMAT));
        String sequencePart = String.format("%0" + SEQUENCE_BITS + "d", sequence.get());
        String machinePart = String.format("%0" + MACHINE_ID_BITS + "d", machineId);

        return timePart + machinePart + sequencePart;
    }

}
